package cn.crystalorigin.plugin

import cn.crystalorigin.plugin.util.getValue
import cn.crystalorigin.plugin.util.instance
import kotlinx.coroutines.runBlocking
import org.reflections.Reflections
import kotlin.reflect.KClass
import kotlin.system.measureTimeMillis

object Loader {
    private val logger by this

    fun init() {
        runBlocking {
            val classes: List<KClass<out AsyncLoader>>
            val time = measureTimeMillis {
                classes = Reflections()
                    .getSubTypesOf(cn.crystalorigin.plugin.AsyncLoader::class.java)
                    .map { it.kotlin }
            }
            logger.info("Search for instance used ${time}ms")
            val (annotated, unannotated) =
               classes
                    .partition { it.annotations.any { a -> a is MustAfter } }
            require(annotated.all { it.annotations.filterIsInstance<MustAfter>()[0].prev in classes })
            val cache = mutableListOf<KClass<out AsyncLoader>>()
            cache.addAll(classes)
            logger.info("Found ${cache.size} classes")
            while (!cache.all {
                    if (it in unannotated) true
                    else cache.indexOf(it.annotations.filterIsInstance<MustAfter>()[0].prev) < cache.indexOf(it)
            }) {
                annotated.forEach { kClz ->
                    val prev = kClz.annotations.filterIsInstance<MustAfter>()[0].prev
                    if (cache.indexOf(kClz) < cache.indexOf(prev)) {
                        logger.info("Find wrong order: ${kClz.instance?.nameAsString} must after ${prev.instance?.nameAsString}")
                        cache.remove(kClz)
                    }
                    val index = cache.indexOf(prev).takeUnless { it == -1 } ?: return@forEach
                    cache.add(index + 1, kClz)
                }
            }
            val result = cache.distinctBy { it.instance?.name }
            logger.info(result.mapNotNull { it.instance?.name }.joinToString("\n", "Loaders:\n"))
            logger.info("Handling ${result.size} loaders")
            result.forEach {
                it.instance?.load()
            }
        }
    }
}